﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Mono.Cecil;
using System.Reflection;
using System.Diagnostics;
using System.Collections;

namespace CustomTypes.Mono.Extensions
{
    
    public static class TypeDefinitionHelper
    {

        
        public static TypeReference GetInterface(this TypeReference type, string nameInterface, bool ignoreCase)
        {

            if (ignoreCase)
                nameInterface = nameInterface.ToLower();

            var tr = type.Resolve();

            if (tr != null)
            {

                var tt = tr.Interfaces;

                foreach (TypeReference item in tt)
                {

                    string name = nameInterface.Contains(".") ? item.FullName : item.Name;

                    if (ignoreCase)
                        name = name.ToLower();

                    if (name == nameInterface)
                        return item;

                }

            }

            return null;

        }
        

        


        //public static MemberReference[] GetMembers(this TypeReference type, string name)
        //{
        //    return GetMembers(type, name, type);
        //}


        //public static MemberReference[] GetMembers(this TypeReference t, string name, TypeReference Sealedtype)
        //{

        //    List<MemberReference> lst = new List<MemberReference>();

        //    TypeDefinition type = t.IsDefinition ? t as TypeDefinition : t.Resolve();

        //    foreach (PropertyDefinition item in type.Properties)
        //        if (string.IsNullOrEmpty(name) || name == item.Name)
        //            lst.Add(item);
                
        //    foreach (FieldDefinition item in type.Fields)
        //        if (string.IsNullOrEmpty(name) || name == item.Name)
        //            lst.Add(item);                

        //    if (type.BaseType != null)
        //    {
        //        TypeReference t2 = type.BaseType;
        //        if (t2 != null)
        //        {
        //            MemberReference[] m2 = GetMembers(t2, name, Sealedtype);
        //            if (m2 != null && m2.Length > 0)                    
        //                lst.AddRange(m2);                    
        //        }
        //    }

        //    return lst.ToArray();

        //}


        public static string FindTypeName(MemberReference property, TypeDefinition sealedtype)
        {

            try
            {

                TypeReference type = property.GetTypeFrom(sealedtype);

                TypeDefinition typeString = type.Module.Import(typeof(string)).Resolve();

                // verifier que les modules sont diffrents
                if (type.HasGenericParameters || (!type.IsPrimitive && type.Module != typeString.Module))
                    return type.AssemblyQualifiedName;
                else
                {

                    if (!string.IsNullOrEmpty(type.FullName))
                        return type.FullName;

                    TypeReference t1 = type.DeclaringType;
                    if (t1.IsGenericInstance)
                    {

                    }

                    return string.Empty;

                }

            }
            catch (Exception)
            {

                throw;
            }


        }


        public static TypeReference GetTypeFrom(this MemberReference member, TypeReference SealedType)
        {


            IMemberType m = member as IMemberType;
            if (m == null)
            {

                if (member.FullName == SealedType.FullName && member is TypeDefinition)
                    return SealedType;

                return null;

            }

            TypeReference retour = m.Type(SealedType);

            //GenericParameter t = retour as GenericParameter;

            //if (t != null)
            //{                
            //    retour = GetTypeFrom(member.DeclaringType, SealedType, t);
            //}
            
            return retour;

        }

        public static TypeReference GetTypeFrom(TypeReference declaringType, TypeReference SealedType, GenericParameter t)
        {

            BoxInherit box = new BoxInherit(null, SealedType);
            bool isInterface = false;

            TypeDefinition t1 = declaringType.Resolve();
            if (t1 != null)
                isInterface = t1.IsInterface;

            var box1 = box.Get(declaringType, isInterface);

            var retour = box1.GetType(t, -1, declaringType, isInterface);

            return retour;

        }



        [DebuggerDisplay("{Name}")]
        internal class BoxInherit
        {

            public TypeReference Type { get; private set; }
            private BoxInherit child;
            public BoxInherit SuperClass { get; private set; }

            public string Name { get { return Type.Name; } }

            public BoxInherit(BoxInherit child, TypeReference type)
            {
                Parameters = new Hashtable();
                this.child = child;
                this.Type = type;




                GenericInstanceType t = Type as GenericInstanceType;

                if (t != null)
                {

                    TypeReference t1 = Type.GetElementType();


                    //var tb1 = t1.GenericParameters.ToArray();
                    //var tb2 = t.GenericArguments.ToArray();
                    //for (int i = 0; i < tb1.Length; i++)
                    //{
                    //    var g = new BoxItemInherit() { Generic = tb1[i] };
                    //    if (tb2.Length>= i)
                    //        g.Type = tb2[i];

                    //    if (g.Type == null || g.Type.IsGenericParameter)
                    //        if (child.Parameters.ContainsKey(g.Generic))                            
                    //            g.Type = (child.Parameters[g.Generic] as BoxItemInherit).Type;
                            
                    //    Parameters.Add(tb1[i], g);

                    //}


                    SuperClass = new BoxInherit(this, t1);

                }
                else
                {
                    

                    //var tb1 = Type.GenericParameters.ToArray();
                    //for (int i = 0; i < tb1.Length; i++)
                    //{                       
                    //    var g = new BoxItemInherit() { Generic = tb1[i] };
                    //    if (child.Parameters.ContainsKey(g.Generic))
                    //        g.Type = (child.Parameters[g.Generic] as BoxItemInherit).Type;
                    //    Parameters.Add(tb1[i], g);
                    //}


                    TypeDefinition t1 = Type.Resolve();
                    if (t1 != null && t1.BaseType != null && t1.BaseType.FullName != "System.Object")
                        SuperClass = new BoxInherit(this, t1.BaseType);
                }

            }


            internal BoxInherit Get(TypeReference typeReference, bool isInterface)
            {

                if (isInterface)
                {
                    TypeReference[] t = GetInterfaces();
                    foreach (var item in t)
                    {
                        if (typeReference.Equals(item))
                            return this;
                    }
                }
                else
                {

                    if (typeReference.Equals(Type))
                        return this;

                    if (SuperClass != null)
                        return SuperClass.Get(typeReference, isInterface);

                }

                return null;

            }

            private TypeReference[] _Interfaces = null;
            private TypeReference[] GetInterfaces()
            {

                if (_Interfaces == null)
                {

                    TypeDefinition t = Type.Resolve();

                    if (t != null)
                        _Interfaces = t.Interfaces.ToArray();
                    else
                        _Interfaces = new TypeReference[0];

                }

                return _Interfaces;

            }

            internal TypeReference GetType(GenericParameter type, int index, TypeReference declaringType, bool isInterface)
            {

                if (type == null)
                    return null;

                GenericInstanceType c = null;

                if (child != null)
                {

                    #region Search on base type

                    c = child.Type as GenericInstanceType;                                        
                    
                    if (c != null)
                    {

                        var i = Type.GenericParameters.IndexOf(type);

                        if (type.Owner.Equals(Type) && i == -1 && c.GenericArguments.Count == 1)                 
                            i = 0;
                        
                        if (i <= c.GenericArguments.Count - 1)
                        {

                            var r = c.GenericArguments[i];
                            if (!r.IsGenericParameter)
                                return r;

                            return child.GetType(r as GenericParameter, i, declaringType, isInterface);

                        }


                    }
                    else if (child != null)
                    {
                        var r = child.Type.GenericParameters[index];
                        var i = child.Type.GenericParameters.IndexOf(r);

                        return child.GetType(r, i, declaringType, isInterface);
                    }

                    #endregion

                }
                else if (isInterface)
                {

                    c = declaringType as GenericInstanceType;
                    if (c != null)
                    {

                        var i = c.GenericArguments.IndexOf(type);
                        if (i >= 0)
                        {

                            c = Type as GenericInstanceType;
                           
                            if (c != null)
                                return c.GenericArguments[i];
                      
                        }
                    }


                }

                

                return null;

            }


            public Hashtable Parameters { get; set; }


        }


        internal class BoxItemInherit
        {

            public GenericParameter Generic { get; set; }

            public TypeReference Type { get; set; }
        }
    }
}
